package com.apop.demo.encrypt.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.apop.demo.encrypt.pojo.DecodeDataDto;
import com.apop.demo.encrypt.pojo.EncodeDataDto;
import com.apop.demo.encrypt.util.sm.FzSM2;
import com.apop.demo.encrypt.util.sm.FzSM3;
import com.apop.demo.encrypt.util.sm.FzSM3WithSM2;
import com.apop.demo.encrypt.util.sm.FzSM4;
import com.apop.demo.encrypt.util.sm.cipher.SMKeyPair;
import lombok.extern.slf4j.Slf4j;


import java.util.Random;

/**
 * 行方提供基于sm234加解操作工具
 *
 * @Description:
 * @Author: zjy
 * @Date: 2025/2/18 14:41
 */
@Slf4j
public class FzSMEncryptUtil {

    /**
     * SM4加密使用的模式
     */
    private static String SM4_ALGORITHM_NAME = "SM4/ECB/PKCS7Padding";

    /**
     * 发送方(银行)保存密钥：SM4密钥、SM2公钥、SM3&SM2私钥
     */
    //private static String sm4Key;
    public static String sm2PubKey;
    public static String sm3PriKey;
    /**
     * 接收方(平台)保存密钥：SM2私钥(本地配置)、SM3&SM2公钥(数据库机构表字段保存)
     */
    public static String sm2PriKey;
    public static String sm3PubKey;

    public static String userId = "A113A017";

    static {
        sm2PubKey = "BPP/1BbsOL+c5Z67pYnlWKVv25G/Ek9P0eGEv54CNKX9yy7EMx9GbF2mgGthUTOsVEYD2hNXIiuEEHvhXm0kTqc=";
        sm2PriKey = "Uqe/JkbQYvdyzWEXQTHtuJ3qw3s9zJZrfWf5kTw38nw=";
        sm3PubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEKu01CrAZis5O6ofOSjxnOXnmza5YUjlkHbUuaeg6Xy8gngE1aSGionJkopPZ252LkCdE90XaA78QBKCNYRemaw==";
        sm3PriKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgQfITrZtqcz+YPmRugqe+Pt6n5NLUPDlyyGmjg+LOq++gCgYIKoEcz1UBgi2hRANCAAQq7TUKsBmKzk7qh85KPGc5eebNrlhSOWQdtS5p6DpfLyCeATVpIaKicmSik9nbnYuQJ0T3RdoDvxAEoI1hF6Zr";
    }

    public static void main(String[] args) throws Exception {
        //generateKey();

        if (true) {
            String originalMsg = "01f5e16499d94eaa85d7dfb5ca96dfd4";
            String signText = getSign(originalMsg, sm3PriKey);
            log.info("signText: {}", signText);
            // 验签
            Boolean b = validateSign(originalMsg, signText, sm3PubKey);
            log.info("验签结果：{}", b);
        }

        if (false) {

            //String oriStr = "{\"pageSize\":\"10\",\"pageIndex\":\"1\",\"fileType\":\"1\",\"filter\":\"\"}";

            // 2.4 保函申请订单业务处理 请求
            String oriStr = "{\"org_id\":\"1888882727397912578\"," +
                    "\"order_no\":\"E2025022700000001\"," +
                    "\"method\":\"bank_apply_guarantee_result\"," +
                    "\"price\":\"99.88\"," +
                    "\"status\":\"1\"}";
            // 订单查询请求
//            String oriStr = "{" +
//                    "\"order_no\":\"E2025021000000006\"," +
//                    "\"method\":\"query_order\"," +
//                    "}";


            String encStr = encode(oriStr);
            String plainStr = decode(encStr);
            //System.out.println("原文："+oriStr);
        }
    }

    public static SMKeyPair generateSM2Key() throws Exception {
        int keyType = 2;
        return FzSM2.genKeyPair(keyType);
    }

    public static SMKeyPair generateSM3WithSm2Key() throws Exception {
        int keyType = 2;
        return FzSM3WithSM2.generateKeyPair(keyType);
    }

    private static void generateKey() throws Exception {
        System.out.println("\n====开始生成密钥====");

        // SM2
        int keyType = 2;
        SMKeyPair keyPair = FzSM2.genKeyPair(keyType);
        sm2PubKey = keyPair.getPubKey();
        sm2PriKey = keyPair.getPriKey();
        System.out.println("SM2公钥(发送方保存)：" + sm2PubKey);
        System.out.println("SM2私钥(接收方保存)：" + sm2PriKey);
//        System.out.println("SM2公钥(发送方保存)：" + HexUtils.byteToHex(sm2PubKey.getBytes()));
//        System.out.println("SM2私钥(接收方保存)：" + HexUtils.byteToHex(sm2PriKey.getBytes()));

        // SM3&SM2
        SMKeyPair smKeyPair = FzSM3WithSM2.generateKeyPair(keyType);
        sm3PubKey = smKeyPair.getPubKey();
        sm3PriKey = smKeyPair.getPriKey();
        System.out.println("SM3&SM2公钥(接收方保存)：" + sm3PubKey);
        System.out.println("SM3&SM2私钥(发送方保存)：" + sm3PriKey);
        System.out.println("====密钥生成结束====");
    }

    public static String encode(String oriStr) throws Exception {
        JSONObject encodeMessage = new JSONObject();
        // appId、渠道编号、交易码
        encodeMessage.put("appId", "A113A017");
        encodeMessage.put("channelCode", "CITICFZ");
        encodeMessage.put("tranCode", "JJDS06");
        encodeMessage.put("timestamp", String.valueOf(System.currentTimeMillis()));

        // nonce：随机数防止重放攻击
        String base = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnopqrstuvwxyz123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 16; ++i) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        String nonce = sb.toString();
        encodeMessage.put("nonce", nonce);
        log.debug("====加密开始====");
        log.debug("key：加密后的sm4密钥");
        String sm4Key = FzSM4.generateKey();
        byte[] encryptedSm4Iv = FzSM2.encrypt(sm2PubKey, 2, sm4Key.getBytes());
        encodeMessage.put("sm4key", Base64Utils.bouncyEncode(encryptedSm4Iv));

        byte[] encryptedMsg = FzSM4.encryptEcb(sm4Key.getBytes(), oriStr.getBytes("UTF-8"), SM4_ALGORITHM_NAME);
        encodeMessage.put("encData", Base64Utils.bouncyEncode(encryptedMsg));

        log.debug("abstract：原始报文的摘要信息");
        byte[] hashMsg = FzSM3.generateSM3HASH(oriStr.getBytes("UTF-8"));
        encodeMessage.put("abstract", Base64Utils.bouncyEncode(hashMsg));

        log.debug("sign：摘要的签名信息");
        byte[] abstractSign = FzSM3WithSM2.sign(sm3PriKey, 2, hashMsg, userId.getBytes());
        encodeMessage.put("sign", Base64Utils.bouncyEncode(abstractSign));

        String result = JSON.toJSONString(encodeMessage);
        log.debug("加密数据报文：" + result);
        log.debug("====加密结束====");
        return result;
    }

    public static String decode(String encData) throws Exception {
        JSONObject encodeMessage = JSONObject.parseObject(encData);
        // 校验
        validateEncodeMessage(encodeMessage);

        String plainMessage;
        log.debug("====解密开始====");
        log.debug("2.解密获得SM4密钥");
        byte[] sm4Key = FzSM2.decrypt(sm2PriKey, 2, Base64Utils.bouncyDecode(encodeMessage.getString("sm4key")));
        log.debug("密钥:" + new String(sm4Key));

        log.debug("3.解密获得原始报文");
        byte[] originMsgBytes = FzSM4.decryptEcb(sm4Key, Base64Utils.bouncyDecode(encodeMessage.getString("encData")), SM4_ALGORITHM_NAME);
        JSONObject originMsg = (JSONObject) JSON.parse(originMsgBytes);
        plainMessage = originMsg.toJSONString();
        log.debug("原始报文:" + plainMessage);


        log.debug("4.对比摘要，确认是否数据丢失或被篡改");
        byte[] hashMsg = FzSM3.generateSM3HASH(originMsgBytes);
        boolean hashResult = Base64Utils.bouncyEncode(hashMsg).equals(encodeMessage.getString("abstract"));
        log.debug("摘要对比结果：" + hashResult);

        log.debug("5.对比签名，确认是否数据来源可信");
        boolean signVerify = FzSM3WithSM2.verify(sm3PubKey, 2, hashMsg, Base64Utils.bouncyDecode(encodeMessage.getString("sign")), userId.getBytes());
        log.debug("签名对比结果：" + signVerify);
        log.debug("====解密结束====");

        return plainMessage;
    }

    /**
     * 验证签名
     *
     * @param originalMsg 原始信息
     * @param sign        签名
     * @param sm3PubKey   公钥
     * @return
     * @throws Exception
     */
    public static Boolean validateSign(String originalMsg, String sign, String sm3PubKey) throws Exception {
        byte[] originMsgBytes = originalMsg.getBytes("UTF-8");
        byte[] hashMsg = FzSM3.generateSM3HASH(originMsgBytes);
        return FzSM3WithSM2.verify(sm3PubKey, 2, hashMsg, Base64Utils.bouncyDecode(sign), userId.getBytes());
    }

    /**
     * 获取签名结果
     *
     * @param originalMsg
     * @param sm3PriKey
     * @return
     * @throws Exception
     */
    public static String getSign(String originalMsg, String sm3PriKey) throws Exception {
        byte[] hashMsg = FzSM3.generateSM3HASH(originalMsg.getBytes("UTF-8"));
        byte[] abstractSign = FzSM3WithSM2.sign(sm3PriKey, 2, hashMsg, userId.getBytes());
        return Base64Utils.bouncyEncode(abstractSign);
    }

    public static String encode(EncodeDataDto dto) throws Exception {
        JSONObject encodeMessage = new JSONObject();
        // appId、渠道编号、交易码
        encodeMessage.put("appId", dto.getAppId());
        encodeMessage.put("channelCode", dto.getChannelCode());
        encodeMessage.put("tranCode", dto.getTranCode());
        encodeMessage.put("timestamp", String.valueOf(System.currentTimeMillis()));

        // nonce：随机数防止重放攻击
        String base = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnopqrstuvwxyz123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 16; ++i) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        String nonce = sb.toString();
        encodeMessage.put("nonce", nonce);
        log.debug("====加密开始====");
        log.debug("key：加密后的sm4密钥");
        String sm4Key = FzSM4.generateKey();
        byte[] encryptedSm4Iv = FzSM2.encrypt(dto.getOppositeSm2PublicKey(), 2, sm4Key.getBytes());
        encodeMessage.put("sm4key", Base64Utils.bouncyEncode(encryptedSm4Iv));

        byte[] encryptedMsg = FzSM4.encryptEcb(sm4Key.getBytes(), dto.getOriginalData().getBytes("UTF-8"), SM4_ALGORITHM_NAME);
        encodeMessage.put("encData", Base64Utils.bouncyEncode(encryptedMsg));

        log.debug("abstract：原始报文的摘要信息");
        byte[] hashMsg = FzSM3.generateSM3HASH(dto.getOriginalData().getBytes("UTF-8"));
        encodeMessage.put("abstract", Base64Utils.bouncyEncode(hashMsg));

        log.debug("sign：摘要的签名信息");
        byte[] abstractSign = FzSM3WithSM2.sign(dto.getSelfSm3PrivateKey(), 2, hashMsg, userId.getBytes());
        encodeMessage.put("sign", Base64Utils.bouncyEncode(abstractSign));

        String result = JSON.toJSONString(encodeMessage);
        log.debug("加密数据报文：" + result);
        log.debug("====加密结束====");
        return result;
    }

    public static String decode(DecodeDataDto dto) throws Exception {
        JSONObject encodeMessage = JSONObject.parseObject(dto.getEncodeData());
        // 校验
        validateEncodeMessage(encodeMessage);

        String plainMessage;
        log.debug("====解密开始====");
        log.debug("2.解密获得SM4密钥");
        byte[] sm4Key = FzSM2.decrypt(dto.getSelfSm2PrivateKey(), 2, Base64Utils.bouncyDecode(encodeMessage.getString("sm4key")));
        log.debug("密钥:" + new String(sm4Key));

        log.debug("3.解密获得原始报文");
        byte[] originMsgBytes = FzSM4.decryptEcb(sm4Key, Base64Utils.bouncyDecode(encodeMessage.getString("encData")), SM4_ALGORITHM_NAME);
        JSONObject originMsg = (JSONObject) JSON.parse(originMsgBytes);
        plainMessage = originMsg.toJSONString();
        log.debug("原始报文:" + plainMessage);


        log.debug("4.对比摘要，确认是否数据丢失或被篡改");
        byte[] hashMsg = FzSM3.generateSM3HASH(originMsgBytes);
        boolean hashResult = Base64Utils.bouncyEncode(hashMsg).equals(encodeMessage.getString("abstract"));
        log.debug("摘要对比结果：" + hashResult);

        log.debug("5.对比签名，确认是否数据来源可信");
        boolean signVerify = FzSM3WithSM2.verify(dto.getOppositeSm3PublicKey(), 2, hashMsg, Base64Utils.bouncyDecode(encodeMessage.getString("sign")), userId.getBytes());
        log.debug("签名对比结果：" + signVerify);
        log.debug("====解密结束====");

        return plainMessage;
    }

    private static void validateEncodeMessage(JSONObject encodeMessage) {
        if (!encodeMessage.containsKey("sm4key")) {
            throw new RuntimeException("sm4key is null");
        }
        if (!encodeMessage.containsKey("encData")) {
            throw new RuntimeException("encData is null");
        }
        if (!encodeMessage.containsKey("abstract")) {
            throw new RuntimeException("abstract is null");
        }
        if (!encodeMessage.containsKey("sign")) {
            throw new RuntimeException("sign is null");
        }


    }
}
